// This file is based on 'caper_generate_cs.cpp'.
// Written by Shinsuke Kishi.
// (2009/03/08)

/* Issues peculiar to Java generator:
 * - '%dont_use_stl' option is merely ignored.
 * - stackOverflow() will never be called in Java parsers. (should remove it?)
 */

#include "caper_ast.hpp"
#include "caper_generate_java.hpp"
#include <algorithm>
#include <cassert>

namespace {

	// Content is exactly same as 'caper_generate_cs.cpp'.
	struct semantic_action_entry
	{
		std::string              name;
		std::vector<std::string> args;

		bool operator<(const semantic_action_entry& x) const
		{
			if(  name < x.name) { return true;  }
			if(x.name <   name) { return false; }
			return args < x.args;
		}
	};

	// Convert primitive type name to wrapper class name.
	const char* const wrapper_name(const std::string& s)
	{
		if(s == "boolean") return "Boolean";
		if(s == "byte"   ) return "Byte";
		if(s == "short"  ) return "Short";
		if(s == "int"    ) return "Integer";
		if(s == "long"   ) return "Long";
		if(s == "float"  ) return "Float";
		if(s == "double" ) return "Double";
		if(s == "char"   ) return "Character";
		return s.c_str();
	}

}  // anonymous namespace

void generate_java(
		const std::string&                     filename,
		std::ostream&                          os,
		const GenerateOptions&                 options,
		const symbol_map_type&                 terminal_types,
		const symbol_map_type&                 nonterminal_types,
		const std::map< size_t, std::string >& token_id_map,
		const action_map_type&                 actions,
		const tgt::parsing_table&              table)
{
	// once header
	os << "// This file was automatically generated by Caper.\n"
	   << "// (http://naoyuki.hirayama.googlepages.com/caper.html)\n\n";

	// namespace header
	os << "package " << options.namespace_name << ";\n\n";

	// using header
	os << "import java.util.*;\n\n";

	// wrapper class
	os << "public class "
	   << filename.substr(0, filename.find("."))
	   << " {\n\n";

	// enum Token
	if(!options.external_token) {
		// token enumeration
		os << options.access_modifier << "	public static enum Token {\n";
		for(size_t i=0; i<token_id_map.size(); ++i) {
			os << "		" << options.token_prefix << (*token_id_map.find(i)).second << ",\n";
		}
		os << "	}\n\n";
	}

	// SemanticAction interface
	std::set<semantic_action_entry> ss;

	for(action_map_type::const_iterator
		it = actions.begin();
		it != actions.end();
		++it)
	{
		const tgt::parsing_table::rule_type& rule = it->first;
		const semantic_action& sa = it->second;

		semantic_action_entry sae;
		sae.name = sa.name;

		// 1st argument = out parameter
		sae.args.push_back((*nonterminal_types.find(rule.left().name())).second);

		for(size_t l=0; l<sa.args.size(); ++l) {
			sae.args.push_back((*sa.args.find(l)).second.type);
		}

		ss.insert(sae);
	}

	std::set<std::string> types;
	for(symbol_map_type::const_iterator
		i = terminal_types.begin();
		i != terminal_types.end();
		++i)
	{
		if((*i).second != "") {
			types.insert((*i).second);
		}
	}

	for(symbol_map_type::const_iterator
		i = nonterminal_types.begin();
		i != nonterminal_types.end();
		++i)
	{
		if((*i).second != "") {
			types.insert((*i).second);
		}
	}

	os << options.access_modifier << "	public static interface SemanticAction {\n"
	   << "		void syntaxError();\n"
	   << "		void stackOverflow();  // Ignore, never called in Java parsers.\n";

	std::set<std::string> methods;

	for(std::set<semantic_action_entry>::const_iterator
		it = ss.begin();
		it != ss.end();
		++it)
	{
		const std::vector<std::string>& args = (*it).args;
		assert(args.size() > 0);
		std::stringstream methodstream;
		methodstream << "		" << args[0] << " " << (*it).name << "(";
		for(size_t l=1; l<args.size(); ++l) {
			if(l != 1) methodstream << ", ";
			methodstream << args[l] << " " << "arg" << l;
		}
		methodstream << ");\n";
		methods.insert(methodstream.str());
	}

	for(std::set<std::string>::const_iterator
		it = methods.begin();
		it != methods.end();
		++it)
	{
		os << *it;
	}

	os << "	}\n\n";

	// Parser class
	os << options.access_modifier << "	public static class Parser {\n\n";

	// StackFrame class
	os << "		private static class StackFrame {\n\n"

	   << "			public final State  state;\n"
	   << "			public final Object value;\n\n"

	   << "			public StackFrame(State state, Object value) {\n"
	   << "				this.state = state;\n"
	   << "				this.value = value;\n"
	   << "			}\n"
	   << "		}\n\n";

	// Stack class
	os << "		private static class Stack {\n\n"

	   << "			private final ArrayList<StackFrame> main = new ArrayList<StackFrame>();\n"
	   << "			private final ArrayList<StackFrame> temp = new ArrayList<StackFrame>();\n"
	   << "			private int gap = 0;\n\n"

	   << "			public Stack() {\n"
	   << "			}\n\n"

	   << "			public void resetTemp() {\n"
	   << "				gap = main.size();\n"
	   << "				temp.clear();\n"
	   << "			}\n\n"

	   << "			public void commitTemp() {\n"
	   << "				main.ensureCapacity(gap + temp.size());\n"
	   << "				main.subList(gap, main.size()).clear();\n"
	   << "				main.addAll(temp);\n"
	   << "			}\n\n"

	   << "			public boolean push(StackFrame f) {\n"
	   << "				temp.add(f);\n"
	   << "				return true;\n"
	   << "			}\n\n"

	   << "			public void pop(int n) {\n"
	   << "				if(temp.size() < n) {\n"
	   << "					n -= temp.size();\n"
	   << "					temp.clear();\n"
	   << "					gap -= n;\n"
	   << "				} else {\n"
	   << "					temp.subList(temp.size() - n, temp.size()).clear();\n"
	   << "				}\n"
	   << "			}\n\n"

	   << "			public StackFrame peek() {\n"
	   << "				if(!temp.isEmpty()) {\n"
	   << "					return temp.get(temp.size() - 1);\n"
	   << "				} else {\n"
	   << "					return main.get(gap - 1);\n"
	   << "				}\n"
	   << "			}\n\n"

	   << "			public StackFrame get(int base, int i) {\n"
	   << "				int n = temp.size();\n"
	   << "				if(base - i <= n) {\n"
	   << "					return temp.get(n - (base - i));\n"
	   << "				} else {\n"
	   << "					return main.get(gap - (base - n) + i);\n"
	   << "				}\n"
	   << "			}\n\n"

	   << "			public void clear() {\n"
	   << "				main.clear();\n"
	   << "			}\n\n"

	   << "		}  // class Stack\n\n";

	// constructor
	os << "		public Parser(SemanticAction sa) {\n"
	   << "			this.sa = sa;\n"
	   << "			reset();\n"
	   << "		}\n\n";

	// public member
	os << "		public void reset() {\n"
	   << "			error = false;\n"
	   << "			accepted = false;\n"
	   << "			stack.clear();\n"
	   << "			stack.resetTemp();\n"
	   << "			if(pushToStack("
	   << "state" << table.first_state() << ", "
	   << "null)) {\n"
	   << "				stack.commitTemp();\n"
	   << "			} else {\n"
	   << "				sa.stackOverflow();\n"
	   << "				error = true;\n"
	   << "			}\n"
	   << "		}\n\n"

	   << "		public boolean post(Token token, Object value) {\n"
	   << "			assert(!error);\n"
	   << "			stack.resetTemp();\n"
	   << "			while(stack.peek().state.state(token, value));\n"
	   << "			if(!error) {\n"
	   << "				stack.commitTemp();\n"
	   << "			}\n"
	   << "			return accepted || error;\n"
	   << "		}\n\n"

	   << "		/**\n"
	   << "		 * The result of isError() is returned together\n"
	   << "		 * in the C++ version and C# version.\n"
	   << "		 * Please call isError() to check in the Java version.\n"
	   << "		 */\n"
	   << "		public Object accept() {\n"
	   << "			assert(accepted || error);\n"
	   << "			if(error)\n"
	   << "				return null;\n"
	   << "			return acceptedValue;\n"
	   << "		}\n\n"

	   << "		public boolean isError() {\n"
	   << "			return error;\n"
	   << "		}\n\n";

	// private member
	os << "		private final Stack stack = new Stack();\n"
	   << "		private final SemanticAction sa;\n"
	   << "		private boolean accepted;\n"
	   << "		private boolean error;\n"
	   << "		private Object acceptedValue;\n\n"

	   << "		private boolean pushToStack(State s, Object v) {\n"
	   << "			assert(!error);\n"
	   << "			if(stack.push(new StackFrame(s, v)))\n"
	   << "				return true;\n"
	   << "			error = true;\n"
	   << "			sa.stackOverflow();\n"
	   << "			return false;\n"
	   << "		}\n\n"

	   << "		private Object getFromStack(int base, int i) {\n"
	   << "			return stack.get(base, i).value;\n"
	   << "		}\n\n";

	// delegate
	os << "		private static interface State {\n"
	   << "			boolean gotof(int i, Object v);\n"
	   << "			boolean state(Token t, Object v);\n"
	   << "		}\n\n";

	// states handler
	for(tgt::parsing_table::states_type::const_iterator
		i = table.states().begin();
		i != table.states().end();
		++i)
	{
		const tgt::parsing_table::state& s = *i;

		os << "		private final State state" << s.no << " = new State() {\n";

		// gotof header
		os << "			public boolean gotof(int nonterminalIndex, Object v) {\n";

		// gotof dispatcher
		std::stringstream ss;
		ss << "				switch(nonterminalIndex) {\n";

		bool output_switch = false;
		std::set<size_t> generated;
		for(tgt::parsing_table::rules_type::const_iterator
			j = table.rules().begin();
			j != table.rules().end();
			++j)
		{
			size_t nonterminal_index = std::distance(
					nonterminal_types.begin(),
					nonterminal_types.find((*j).left().name()));

			if(generated.find(nonterminal_index) != generated.end()) {
				continue;
			}

			tgt::parsing_table::state::goto_table_type::const_iterator k =
				(*i).goto_table.find((*j).left());

			if(k != (*i).goto_table.end()) {
				ss << "				case " << nonterminal_index << ": "
				   << "return pushToStack("
				   << "state" << (*k).second << ", "
				   << "v);\n";
				output_switch = true;
				generated.insert(nonterminal_index);
			}
		}

		ss << "				default:\n"
		   << "					assert(false);\n"
		   << "					return false;\n"
		   << "				}\n";

		if(output_switch) {
			os << ss.str();
		} else {
			os << "				assert(false);\n"
			   << "				return false;\n";
		}

		// gotof footer
		os << "			}\n";

		// state header
		os << "			public boolean state(Token token, Object value) {\n";

		// dispatcher header
		os << "				switch(token) {\n";

		// action table
		for(tgt::parsing_table::state::action_table_type::const_iterator
			j = s.action_table.begin();
			j != s.action_table.end();
			++j)
		{
			// action header
			os << "				case " << options.token_prefix
			   << (*token_id_map.find((*j).first)).second << ":\n";

			// action
			const tgt::parsing_table::action* a = &(*j).second;
			switch(a->type) {
			case zw::gr::action_shift:
				os << "					// shift\n"
				   << "					pushToStack("
				   << "state" << a->dest_index << ", "
				   << "value);\n"
				   << "					return false;\n";
				break;
			case zw::gr::action_reduce:
				{
					size_t base = table.rules()[a->rule_index].right().size();

					const tgt::parsing_table::rule_type& rule = table.rules()[a->rule_index];
					action_map_type::const_iterator k = actions.find(rule);

					size_t nonterminal_index = std::distance(
							nonterminal_types.begin(),
							nonterminal_types.find(rule.left().name()));

					if(k != actions.end()) {
						const semantic_action& sa = (*k).second;

						os << "				{\n"
						   << "					// reduce\n";

						// automatic argument conversion
						for(size_t l=0; l<sa.args.size(); ++l) {
							const semantic_action_argument& arg =
								(*sa.args.find(l)).second;
							os << "					" << arg.type << " arg" << l
							   << " = (" << wrapper_name(arg.type) << ")getFromStack(" << base
							   << ", " << arg.src_index << ");\n";
						}

						// semantic action
						const std::string& rtype =
							(*nonterminal_types.find(rule.left().name())).second;
						os << "					"
						   << rtype
						   << " r = sa." << sa.name << "(";
						for(size_t l=0; l<sa.args.size(); ++l) {
							if(l != 0) os << ", ";
							os << "arg" << l;
						}
						os << ");\n";

						// automatic return value conversion
						os << "					stack.pop(" << base << ");\n"
						   << "					return stack.peek().state.gotof("
						   << nonterminal_index << ", r);\n"
						   << "				}\n";
					} else {
						os << "					// reduce\n"
						// << "					// run_semantic_action();\n"
						   << "					stack.pop(" << base << ");\n"
						   << "					return stack.peek().state.gotof("
						   << nonterminal_index << ", null);\n";
					}
				}
				break;
			case zw::gr::action_accept:
				os << "					// accept\n"
				// << "					// run_semantic_action();\n"
				   << "					accepted = true;\n"
				   << "					acceptedValue = getFromStack(1, 0);\n"  // implicit root
				   << "					return false;\n";
				break;
			case zw::gr::action_error:
				os << "					sa.syntaxError();\n"
				   << "					error = true;\n"
				   << "					return false;\n";
				break;
			}
			// action footer
		}

		// dispatcher footer
		os << "				default:\n"
		   << "					sa.syntaxError();\n"
		   << "					error = true;\n"
		   << "					return false;\n"
		   << "				}\n";

		// state footer
		os << "			}\n";

		os << "		};\n\n";
	}

	os << "	}  // class Parser\n\n";

	os << "}  // wrapper class\n";

	// once footer
}
